import React, { Component } from 'react';
import {
  API_BASE,
  SafeTextarea,
  PromotionBar,
  HighlightedMarkdown,
} from './Common';
import { MessageViewer } from './Message';
import { LoginPopup } from './infrastructure/widgets';
import { ColorPicker } from './color_picker';
import { ConfigUI } from './Config';
import fixOrientation from 'fix-orientation';
import copy from 'copy-to-clipboard';
import { cache } from './cache';
import {
  API_VERSION_PARAM,
  THUHOLE_API_ROOT,
  API,
  get_json,
  token_param,
} from './flows_api';

import './UserAction.css';

const BASE64_RATE = 4 / 3;
const MAX_IMG_DIAM = 8000;
const MAX_IMG_PX = 5000000;
const MAX_IMG_FILESIZE = 450000 * BASE64_RATE;

export const TokenCtx = React.createContext({
  value: null,
  set_value: () => {},
});

// class LifeInfoBox extends Component {
//     constructor(props) {
//         super(props);
//         if(!window._life_info_cache)
//             window._life_info_cache={};
//         this.CACHE_TIMEOUT_S=15;
//         this.state={
//             today_info: this.cache_get('today_info'),
//             card_balance: this.cache_get('card_balance'),
//             net_balance: this.cache_get('net_balance'),
//             mail_count: this.cache_get('mail_count'),
//         };
//         this.INTERNAL_NETWORK_FAILURE='_network_failure';
//         this.API_NAME={
//             today_info: 'hole/today_info',
//             card_balance: 'isop/card_balance',
//             net_balance: 'isop/net_balance',
//             mail_count: 'isop/mail_count',
//         };
//     }
//
//     cache_get(key) {
//         let cache_item=window._life_info_cache[key];
//         if(!cache_item || (+new Date())-cache_item[0]>1000*this.CACHE_TIMEOUT_S)
//             return null;
//         else
//             return cache_item[1];
//     }
//     cache_set(key,value) {
//         if(!window._life_info_cache[key] || window._life_info_cache[key][1]!==value)
//             window._life_info_cache[key]=[+new Date(),value];
//     }
//
//     load(state_key) {
//         this.setState({
//             [state_key]: null,
//         },()=>{
//             fetch(
//                 PKUHELPER_ROOT+'api_xmcp/'+this.API_NAME[state_key]
//                 +'?user_token='+encodeURIComponent(this.props.token)
//                 +API_VERSION_PARAM()
//             )
//                 .then(get_json)
//                 .then((json)=>{
//                     //console.log(json);
//                     this.setState({
//                         [state_key]: json,
//                     });
//                 })
//                 .catch((e)=>{
//                     this.setState({
//                         [state_key]: {
//                             errMsg: '网络错误 '+e,
//                             errCode: this.INTERNAL_NETWORK_FAILURE,
//                             success: false,
//                         }
//                     });
//                 })
//         });
//     }
//
//     componentDidMount() {
//         ['today_info','card_balance','net_balance','mail_count'].forEach((k)=>{
//             if(!this.state[k])
//                 this.load(k);
//         });
//     }
//
//     reload_all() {
//         ['today_info','card_balance','net_balance','mail_count'].forEach((k)=>{
//             this.load(k);
//         });
//     }
//
//     render_line(state_key,title,value_fn,action,url_fn,do_login) {
//         let s=this.state[state_key];
//         if(!s)
//             return (
//                 <tr>
//                     <td>{title}</td>
//                     <td>加载中……</td>
//                     <td />
//                 </tr>
//             );
//         else if(!s.success) {
//             let type='加载失败';
//             if(s.errCode===this.INTERNAL_NETWORK_FAILURE)
//                 type='网络错误';
//             else if(['E01','E02','E03'].indexOf(s.errCode)!==-1)
//                 type='授权失效';
//
//             let details=JSON.stringify(s);
//             if(s.errMsg)
//                 details=s.errMsg;
//             else if(s.error)
//                 details=s.error;
//
//             return (
//                 <tr>
//                     <td>{title}</td>
//                     <td className="life-info-error">
//                         <a onClick={()=>alert(details)}>{type}</a>
//                     </td>
//                     <td>
//                         {type==='授权失效' ?
//                             <a onClick={do_login}>
//                                 <span className="icon icon-forward" />&nbsp;重新登录
//                             </a> :
//                             <a onClick={()=>this.load(state_key)}>
//                                 <span className="icon icon-forward" />&nbsp;重试
//                             </a>
//                         }
//                     </td>
//                 </tr>
//             )
//         }
//         else {
//             this.cache_set(state_key,s);
//
//             return (
//                 <tr>
//                     <td>{title}</td>
//                     <td>{value_fn(s)}</td>
//                     <td>
//                         <a href={url_fn(s)} target="_blank">
//                             <span className="icon icon-forward" />&nbsp;{action}
//                         </a>
//                     </td>
//                 </tr>
//             );
//         }
//     }
//
//     render() {
//         return (
//             <LoginPopup token_callback={(t)=>{
//                 this.props.set_token(t);
//                 this.reload_all();
//             }}>{(do_login)=>(
//                 <div className="box">
//                     <table className="life-info-table">
//                         <tbody>
//                             {this.render_line(
//                                 'today_info',
//                                 '今日',(s)=>s.info,
//                                 '校历',(s)=>s.schedule_url,
//                                 do_login,
//                             )}
//                             {this.render_line(
//                                 'card_balance',
//                                 '校园卡',(s)=>`余额￥${s.balance.toFixed(2)}`,
//                                 '充值',()=>'https://virtualprod.alipay.com/educate/educatePcRecharge.htm?schoolCode=PKU&schoolName=',
//                                 do_login,
//                             )}
//                             {this.render_line(
//                                 'net_balance',
//                                 '网费',(s)=>`余额￥${s.balance.toFixed(2)}`,
//                                 '充值',()=>'https://its.pku.edu.cn/epay.jsp',
//                                 do_login,
//                             )}
//                             {this.render_line(
//                                 'mail_count',
//                                 '邮件',(s)=>`未读 ${s.count} 封`,
//                                 '查看',()=>'https://mail.pku.edu.cn/',
//                                 do_login,
//                             )}
//                         </tbody>
//                     </table>
//                 </div>
//             )}</LoginPopup>
//         )
//     }
// }

export function InfoSidebar(props) {
  return (
    <div>
      <PromotionBar />
      <LoginForm show_sidebar={props.show_sidebar} />
      <div className="box list-menu">
        <a
          onClick={() => {
            props.show_sidebar('设置', <ConfigUI />);
          }}
        >
          <span className="icon icon-settings" />
          <label>设置</label>
        </a>
        &nbsp;&nbsp;
        <a href="https://thuhole.com/policy.html" target="_blank">
          <span className="icon icon-textfile" />
          <label>树洞规范（试行）</label>
        </a>
        &nbsp;&nbsp;
        <a
          href="https://github.com/thuhole/thuhole-go-backend/issues"
          target="_blank"
        >
          <span className="icon icon-github" />
          <label>意见反馈</label>
        </a>
      </div>
      <div className="box help-desc-box">
        <p>
          <a
            onClick={() => {
              if ('serviceWorker' in navigator) {
                navigator.serviceWorker
                  .getRegistrations()
                  .then((registrations) => {
                    for (let registration of registrations) {
                      console.log('unregister', registration);
                      registration.unregister();
                    }
                  });
              }
              cache().clear();
              setTimeout(() => {
                window.location.reload(true);
              }, 200);
            }}
          >
            强制检查更新
          </a>
          （当前版本：【{process.env.REACT_APP_BUILD_INFO || '---'}{' '}
          {process.env.NODE_ENV}】 会自动在后台检查更新并在下次访问时更新）
        </p>
      </div>
      <div className="box help-desc-box">
        <p>联系我们：thuhole at protonmail dot com</p>
      </div>
      <div className="box help-desc-box">
        <p>
          T大树洞 网页版 by @thuhole， 基于&nbsp;
          <a
            href="https://www.gnu.org/licenses/gpl-3.0.zh-cn.html"
            target="_blank"
          >
            GPLv3
          </a>
          &nbsp;协议在{' '}
          <a href="https://github.com/thuhole/webhole" target="_blank">
            GitHub
          </a>{' '}
          开源
        </p>
        <p>
          T大树洞 网页版的诞生离不开&nbsp;
          <a
            href="https://github.com/pkuhelper-web/webhole"
            target="_blank"
            rel="noopener"
          >
            P大树洞网页版 by @xmcp
          </a>
          、
          <a href="https://reactjs.org/" target="_blank" rel="noopener">
            React
          </a>
          、
          <a href="https://icomoon.io/#icons" target="_blank" rel="noopener">
            IcoMoon
          </a>
          &nbsp;等开源项目
        </p>
        <p>
          This program is free software: you can redistribute it and/or modify
          it under the terms of the GNU General Public License as published by
          the Free Software Foundation, either version 3 of the License, or (at
          your option) any later version.
        </p>
        <p>
          This program is distributed in the hope that it will be useful, but
          WITHOUT ANY WARRANTY; without even the implied warranty of
          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&nbsp;
          <a
            href="https://www.gnu.org/licenses/gpl-3.0.zh-cn.html"
            target="_blank"
          >
            GNU General Public License
          </a>
          &nbsp;for more details.
        </p>
      </div>
    </div>
  );
}

class ResetUsertokenWidget extends Component {
  constructor(props) {
    super(props);
    this.state = {
      loading_status: 'done',
    };
  }

  do_reset() {
    if (
      window.confirm(
        '您正在重置 UserToken！\n您的账号将会在【所有设备】上注销，您需要手动重新登录！',
      )
    ) {
      let uid = window.prompt(
        '您正在重置 UserToken！\n请输入您的学号以确认身份：',
      );
      if (uid)
        this.setState(
          {
            loading_status: 'loading',
          },
          () => {
            fetch(THUHOLE_API_ROOT + 'api_xmcp/hole/reset_usertoken', {
              method: 'post',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                user_token: this.props.token,
                uid: uid,
              }),
            })
              .then(get_json)
              .then((json) => {
                if (json.error) throw new Error(json.error);
                else alert('重置成功！您需要在所有设备上重新登录。');

                this.setState({
                  loading_status: 'done',
                });
              })
              .catch((e) => {
                alert('重置失败：' + e);
                this.setState({
                  loading_status: 'done',
                });
              });
          },
        );
    }
  }

  render() {
    if (this.state.loading_status === 'done')
      return <a onClick={this.do_reset.bind(this)}>重置</a>;
    else if (this.state.loading_status === 'loading')
      return (
        <a>
          <span className="icon icon-loading" />
        </a>
      );
  }
}

export class LoginForm extends Component {
  copy_token(token) {
    if (copy(token)) alert('复制成功！\n请一定不要泄露哦');
  }

  render() {
    return (
      <TokenCtx.Consumer>
        {(token) => (
          <div>
            {/*{!!token.value &&*/}
            {/*    <LifeInfoBox token={token.value} set_token={token.set_value} />*/}
            {/*}*/}
            <div className="login-form box">
              {token.value ? (
                <div>
                  <p>
                    <b>您已登录。</b>
                    <button
                      type="button"
                      onClick={() => {
                        token.set_value(null);
                      }}
                    >
                      <span className="icon icon-logout" /> 注销
                    </button>
                    <br />
                  </p>
                  {/*<p>*/}
                  {/*根据计算中心要求，访问授权三个月内有效，过期需重新登录。*/}
                  {/*T大树洞将会单向加密(i.e. 哈希散列)您的邮箱后再存入数据库，因此您的发帖具有较强的匿名性。具体可见我们的<a href="https://github.com/thuhole/thuhole-go-backend/blob/76f56e6b75257b59e552b6bdba77e114151fcad1/src/db.go#L184">后端开源代码</a>。*/}
                  {/*</p>*/}
                  <p>
                    <a
                      onClick={() => {
                        this.props.show_sidebar(
                          '系统消息',
                          <MessageViewer token={token.value} />,
                        );
                      }}
                    >
                      查看系统消息
                    </a>
                    <br />
                    当您发送的内容违规时，我们将用系统消息提示您
                  </p>
                  <p>
                    <a onClick={this.copy_token.bind(this, token.value)}>
                      复制 User Token
                    </a>
                    <br />
                    复制 User Token
                    可以在新设备登录，切勿告知他人。若怀疑被盗号请重新邮箱验证码登录以重置Token。
                    {/*，若怀疑被盗号请尽快 <ResetUsertokenWidget token={token.value} />*/}
                  </p>
                </div>
              ) : (
                <LoginPopup token_callback={token.set_value}>
                  {(do_popup) => (
                    <div>
                      <p>
                        <button type="button" onClick={do_popup}>
                          <span className="icon icon-login" />
                          &nbsp;登录
                        </button>
                      </p>
                      <p>
                        <small>
                          T大树洞
                          面向T大学生，通过T大邮箱验证您的身份并提供服务。
                        </small>
                      </p>
                    </div>
                  )}
                </LoginPopup>
              )}
            </div>
          </div>
        )}
      </TokenCtx.Consumer>
    );
  }
}

export class ReplyForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: '',
      loading_status: 'done',
      preview: false,
    };
    this.on_change_bound = this.on_change.bind(this);
    this.area_ref = this.props.area_ref || React.createRef();
    this.global_keypress_handler_bound = this.global_keypress_handler.bind(
      this,
    );
    this.color_picker = new ColorPicker();
  }

  global_keypress_handler(e) {
    if (
      e.code === 'Enter' &&
      !e.ctrlKey &&
      !e.altKey &&
      ['input', 'textarea'].indexOf(e.target.tagName.toLowerCase()) === -1
    ) {
      if (this.area_ref.current) {
        e.preventDefault();
        this.area_ref.current.focus();
      }
    }
  }
  componentDidMount() {
    document.addEventListener('keypress', this.global_keypress_handler_bound);
  }
  componentWillUnmount() {
    document.removeEventListener(
      'keypress',
      this.global_keypress_handler_bound,
    );
  }

  on_change(value) {
    this.setState({
      text: value,
    });
  }

  on_submit(event) {
    if (event) event.preventDefault();
    if (this.state.loading_status === 'loading') return;
    this.setState({
      loading_status: 'loading',
    });

    let data = new URLSearchParams();
    data.append('pid', this.props.pid);
    data.append('text', this.state.text);
    data.append('user_token', this.props.token);
    fetch(
      API_BASE + '/api.php?action=docomment' + token_param(this.props.token),
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: data,
      },
    )
      .then(get_json)
      .then((json) => {
        if (json.code !== 0) {
          if (json.msg) alert(json.msg);
          throw new Error(JSON.stringify(json));
        }

        this.setState({
          loading_status: 'done',
          text: '',
          preview: false,
        });
        this.area_ref.current.clear();
        this.props.on_complete();
      })
      .catch((e) => {
        console.error(e);
        alert('回复失败');
        this.setState({
          loading_status: 'done',
        });
      });
  }

  toggle_preview() {
    this.setState({
      preview: !this.state.preview,
    });
  }

  render() {
    return (
      <form
        onSubmit={this.on_submit.bind(this)}
        className={'reply-form box' + (this.state.text ? ' reply-sticky' : '')}
      >
        {this.state.preview ? (
          <div className="reply-preview">
            <HighlightedMarkdown
              text={this.state.text}
              color_picker={this.color_picker}
              show_pid={() => {}}
            />
          </div>
        ) : (
          <SafeTextarea
            ref={this.area_ref}
            id={this.props.pid}
            on_change={this.on_change_bound}
            on_submit={this.on_submit.bind(this)}
          />
        )}
        <button
          type="button"
          onClick={() => {
            this.toggle_preview();
          }}
        >
          {this.state.preview ? (
            <span className="icon icon-eye-blocked" />
          ) : (
            <span className="icon icon-eye" />
          )}
        </button>
        {this.state.loading_status === 'loading' ? (
          <button disabled="disabled">
            <span className="icon icon-loading" />
          </button>
        ) : (
          <button type="submit">
            <span className="icon icon-send" />
          </button>
        )}
      </form>
    );
  }
}

export class PostForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: '',
      loading_status: 'done',
      img_tip: null,
      preview: false,
    };
    this.img_ref = React.createRef();
    this.area_ref = React.createRef();
    this.on_change_bound = this.on_change.bind(this);
    this.on_img_change_bound = this.on_img_change.bind(this);
    this.color_picker = new ColorPicker();
  }

  componentDidMount() {
    if (this.area_ref.current) this.area_ref.current.focus();
  }

  on_change(value) {
    this.setState({
      text: value,
    });
  }

  do_post(text, img) {
    let data = new URLSearchParams();
    data.append('text', this.state.text);
    data.append('type', img ? 'image' : 'text');
    data.append('user_token', this.props.token);
    if (img) data.append('data', img);

    fetch(API_BASE + '/api.php?action=dopost' + token_param(this.props.token), {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: data,
    })
      .then(get_json)
      .then((json) => {
        if (json.code !== 0) {
          if (json.msg) alert(json.msg);
          throw new Error(JSON.stringify(json));
        }

        this.setState({
          loading_status: 'done',
          text: '',
          preview: false,
        });
        this.area_ref.current.clear();
        this.props.on_complete();
      })
      .catch((e) => {
        console.error(e);
        alert('发表失败');
        this.setState({
          loading_status: 'done',
        });
      });
  }

  proc_img(file) {
    return new Promise((resolve, reject) => {
      function return_url(url) {
        const idx = url.indexOf(';base64,');
        if (idx === -1) throw new Error('img not base64 encoded');

        return url.substr(idx + 8);
      }

      let reader = new FileReader();
      function on_got_img(url) {
        const image = new Image();
        image.onload = () => {
          let width = image.width;
          let height = image.height;
          let compressed = false;

          if (width > MAX_IMG_DIAM) {
            height = (height * MAX_IMG_DIAM) / width;
            width = MAX_IMG_DIAM;
            compressed = true;
          }
          if (height > MAX_IMG_DIAM) {
            width = (width * MAX_IMG_DIAM) / height;
            height = MAX_IMG_DIAM;
            compressed = true;
          }
          if (height * width > MAX_IMG_PX) {
            let rate = Math.sqrt((height * width) / MAX_IMG_PX);
            height /= rate;
            width /= rate;
            compressed = true;
          }
          console.log('chosen img size', width, height);

          let canvas = document.createElement('canvas');
          let ctx = canvas.getContext('2d');
          canvas.width = width;
          canvas.height = height;
          ctx.drawImage(image, 0, 0, width, height);

          let quality_l = 0.1,
            quality_r = 0.9,
            quality,
            new_url;
          while (quality_r - quality_l >= 0.03) {
            quality = (quality_r + quality_l) / 2;
            new_url = canvas.toDataURL('image/jpeg', quality);
            console.log(
              quality_l,
              quality_r,
              'trying quality',
              quality,
              'size',
              new_url.length,
            );
            if (new_url.length <= MAX_IMG_FILESIZE) quality_l = quality;
            else quality_r = quality;
          }
          if (quality_l >= 0.101) {
            console.log('chosen img quality', quality);
            resolve({
              img: return_url(new_url),
              quality: quality,
              width: Math.round(width),
              height: Math.round(height),
              compressed: compressed,
            });
          } else {
            reject('图片过大，无法上传');
          }
        };
        image.src = url;
      }
      reader.onload = (event) => {
        fixOrientation(event.target.result, {}, (fixed_dataurl) => {
          on_got_img(fixed_dataurl);
        });
      };
      reader.readAsDataURL(file);
    });
  }

  on_img_change() {
    if (this.img_ref.current && this.img_ref.current.files.length)
      this.setState(
        {
          img_tip: '（正在处理图片……）',
        },
        () => {
          this.proc_img(this.img_ref.current.files[0])
            .then((d) => {
              this.setState({
                img_tip:
                  `（${d.compressed ? '压缩到' : '尺寸'} ${d.width}*${
                    d.height
                  } / ` +
                  `质量 ${Math.floor(d.quality * 100)}% / ${Math.floor(
                    d.img.length / BASE64_RATE / 1000,
                  )}KB）`,
              });
            })
            .catch((e) => {
              this.setState({
                img_tip: `图片无效：${e}`,
              });
            });
        },
      );
    else
      this.setState({
        img_tip: null,
      });
  }

  on_submit(event) {
    if (event) event.preventDefault();
    if (this.state.loading_status === 'loading') return;
    if (this.img_ref.current.files.length) {
      this.setState({
        loading_status: 'processing',
      });
      this.proc_img(this.img_ref.current.files[0])
        .then((d) => {
          this.setState({
            loading_status: 'loading',
          });
          this.do_post(this.state.text, d.img);
        })
        .catch((e) => {
          alert(e);
        });
    } else {
      this.setState({
        loading_status: 'loading',
      });
      this.do_post(this.state.text, null);
    }
  }

  toggle_preview() {
    this.setState({
      preview: !this.state.preview,
    });
  }

  render() {
    return (
      <form onSubmit={this.on_submit.bind(this)} className="post-form box">
        <div className="post-form-bar">
          <label>
            图片
            <input
              ref={this.img_ref}
              type="file"
              accept="image/*"
              disabled={this.state.loading_status !== 'done'}
              onChange={this.on_img_change_bound}
            />
          </label>

          {this.state.preview ? (
            <button
              type="button"
              onClick={() => {
                this.toggle_preview();
              }}
            >
              <span className="icon icon-eye-blocked" />
              &nbsp;编辑
            </button>
          ) : (
            <button
              type="button"
              onClick={() => {
                this.toggle_preview();
              }}
            >
              <span className="icon icon-eye" />
              &nbsp;预览
            </button>
          )}

          {this.state.loading_status !== 'done' ? (
            <button disabled="disabled">
              <span className="icon icon-loading" />
              &nbsp;
              {this.state.loading_status === 'processing' ? '处理' : '上传'}
            </button>
          ) : (
            <button type="submit">
              <span className="icon icon-send" />
              &nbsp;发表
            </button>
          )}
        </div>
        {!!this.state.img_tip && (
          <p className="post-form-img-tip">
            <a
              onClick={() => {
                this.img_ref.current.value = '';
                this.on_img_change();
              }}
            >
              删除图片
            </a>
            {this.state.img_tip}
          </p>
        )}
        {this.state.preview ? (
          <div className="post-preview">
            <HighlightedMarkdown
              text={this.state.text}
              color_picker={this.color_picker}
              show_pid={() => {}}
            />
          </div>
        ) : (
          <SafeTextarea
            ref={this.area_ref}
            id="new_post"
            on_change={this.on_change_bound}
            on_submit={this.on_submit.bind(this)}
          />
        )}
        <p>
          <small>
            请遵守
            <a href="https://thuhole.com/policy.html" target="_blank">
              树洞管理规范（试行）
            </a>
            ，文明发言
          </small>
        </p>
      </form>
    );
  }
}
